package com.hooper.redisdemo.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hooper.redisdemo.domain.User;
import com.hooper.redisdemo.mapper.UserMapper;
import com.hooper.redisdemo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static com.hooper.redisdemo.constant.UserConstant.*;

/**
 * @author Mind
 * @description 针对表【tb_user】的数据库操作Service实现
 * @createDate 2023-02-24 12:35:57
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Resource
    private RedisTemplate<String, Long> redisTemplate;


    @Override
    public String followOrUnfollow(Long follower, Long target) {
        if (follower.equals(target)) {
            return "不可关注自己！";
        }
        String followSetKey = FOLLOW_KEY_PREFIX + follower;
        String fansSetKey = USER_FAN_KEY_PREFIX + target;
        SetOperations<String, Long> setOps = redisTemplate.opsForSet();
        boolean hasFollowed = Boolean.TRUE.equals(setOps.isMember(followSetKey, target));
        //若该用户已关注此人，则为取消关注请求
        if (hasFollowed) {
            setOps.remove(followSetKey, target);
            setOps.remove(fansSetKey, follower);
        } else {
            //将用户关注信息存入关注列表
            setOps.add(followSetKey, target);
            //将粉丝信息存入粉丝列表
            setOps.add(fansSetKey, follower);
        }
        syncFollowDataToDB(follower, target, followSetKey, fansSetKey, setOps);
        return hasFollowed ? "已取消关注" : "关注成功！";
    }

    private void syncFollowDataToDB(Long follower, Long target, String followSetKey, String fansSetKey, SetOperations<String, Long> setOps) {
        Set<Long> followers = setOps.members(fansSetKey);
        Set<Long> targets = setOps.members(followSetKey);
        log.info("followers:{}", followers);
        log.info("targets:{}", targets);
        String fanListJson = JSONUtil.toJsonStr(followers);
        String followListJson = JSONUtil.toJsonStr(targets);
        ArrayList<User> list = new ArrayList<>(2);
        User targetUser = new User(), fanUser = new User();
        targetUser.setId(target);
        targetUser.setFansList(fanListJson);
        fanUser.setId(follower);
        fanUser.setFollowList(followListJson);
        list.add(targetUser);
        list.add(fanUser);
        this.updateBatchById(list);
    }

    @Override
    public Set<User> getMutualFollows(Long one, Long another) {
        if (one.equals(another)) {
            return Collections.emptySet();
        }
        SetOperations<String, Long> setOps = redisTemplate.opsForSet();
        String oneFollowKey = FOLLOW_KEY_PREFIX + one;
        String anotherFollowKey = FOLLOW_KEY_PREFIX + another;
        Set<Long> mutualFollow = setOps.intersect(oneFollowKey, anotherFollowKey);
        mutualFollow = Optional.ofNullable(mutualFollow).orElseGet(HashSet::new);
        if (mutualFollow.size() == 0) {
            return Collections.emptySet();
        }
        List<User> userList = this.listByIds(mutualFollow);
        userList = Optional.ofNullable(userList).orElseGet(Collections::emptyList);
        return new HashSet<>(userList);
    }

    @Override
    public String userSignIn(Long userId, int dayOfMoth) {
        log.info("userId:{}\tday:{}", userId, dayOfMoth);
        dayOfMoth -= 1;
        LocalDate now = LocalDate.now();
        String date = DateTimeFormatter.ofPattern("yyyy:MM:").format(now);
        String signKey = SIGN_KEY_PREFIX + date + userId;
        Boolean isSigned = redisTemplate.opsForValue().getBit(signKey, dayOfMoth);
        if (Boolean.TRUE.equals(isSigned)) {
            return "一天一签, 不可重复签到！";
        }
        redisTemplate.opsForValue().setBit(signKey, dayOfMoth, true);
        Long bitCount = redisTemplate.execute((RedisCallback<Long>)
                cbk -> cbk.bitCount(signKey.getBytes()));
        log.info("count:{}", bitCount);
        return "签到成功！ \n本月签到天数" + bitCount;
    }

    @Override
    public String consecutiveSignDays(Long userId) {
        LocalDate now = LocalDate.now();
        String date = DateTimeFormatter.ofPattern("yyyy:MM:").format(now);
        String signKey = SIGN_KEY_PREFIX + date + userId;
        List<Long> unsignedNum = redisTemplate.opsForValue().bitField(signKey, BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(now.getDayOfMonth())).valueAt(0));
        if (CollectionUtils.isEmpty(unsignedNum)) {
            return "该用户无任何签到记录！";
        }
        long result = Optional.ofNullable(unsignedNum.get(0)).orElse(0L);
        log.info("数值结果：{}", result);
        int count = getMaxConsecutiveOnes((int) result);
        return "本月最多连续签到次数为：" + count;
    }

    /**
     * 求出一个数的二进制位中最大1连续个数
     */
    private static int getMaxConsecutiveOnes(int num) {
        int count = 0, maxCount = 0;
        while (num > 0) {
            if ((num & 1) == 1) {
                count++;
                maxCount = Math.max(maxCount, count);
            } else {
                count = 0;
            }
            num >>>= 1;
        }
        return maxCount;
    }
}




